﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using blackhouse.WebNemo.Data;
using System.Data;
using System.Globalization;
using System.Runtime.Caching;
using blackhouse.WebNemo.Data.Cache;

namespace blackhouse.WebNemo.Business.Groups {
    public class DaGroup : IGroup, IGroupOperations {

        #region Fields

        private IDataAccess data;
        private ImpGroup instance;

        #endregion

        #region Properties

        public int Id {
            get { return this.instance.Id; }
            private set { this.instance.Id = value; }
        }

        public string Name {
            get { return this.instance.Name; }
            set { this.instance.SetName(value); }
        }

        public string Identyficator {
            get { return this.instance.Identyficator; }
            set { this.instance.Identyficator = value; }
        }

        public string SystemName {
            get { return this.instance.SystemName; }
            private set { this.instance.SystemName = value; }
        }

        public bool Exists {
            get { return this.instance.Exists; }
            private set { this.instance.Exists = value; }
        }

        public GroupColumnCollection Columns {
            get {
                if (this.instance.Columns.Count == 0)
                    this.SetColumns(this.instance.Columns);
                return this.instance.Columns;
            }
        }

        public string SelectTable {
            get { return this.instance.SelectTable; }
            private set { this.instance.SelectTable = value; }
        }

        public GroupType Type {
            get { return this.instance.Type; }
            private set { this.instance.Type = value; }
        }

        public bool EnableTranslate {
            get { return this.instance.EnableTranslate; }
            set { this.instance.EnableTranslate = value; }
        }

        public bool ReadOnly {
            get { return this.instance.ReadOnly; }
            set { this.instance.ReadOnly = value; }
        }

        #endregion

        #region Constructors

        public DaGroup(IDataAccess dataAccess) {
            this.data = dataAccess;
            this.instance = new ImpGroup();
        }

        private DaGroup(IDataAccess dataAccess, GroupData data) {
            this.data = dataAccess;
            this.instance = this.ReadInstance(data);
        }

        public DaGroup(IDataAccess dataAccess, int groupId) {
            this.data = dataAccess;
            this.instance = this.ReadInstance(groupId);
        }

        #endregion

        #region Methods

        private void SetColumns(GroupColumnCollection columns) {
            columns.Clear();

            DataTable tableScheme = this.data.GetGroupScheme(this.GetFromGroups());
            GroupColumnData[] gcdc = this.data.GetColumns(this.Id);

            foreach (GroupColumnData gcd in gcdc) {
                DataColumn dc = tableScheme.Columns[gcd.ColumnName];
                if (gcd.ColumnType == "FileField")
                    dc = tableScheme.Columns[gcd.ColumnName + "_name"];
                GroupColumn gc = new GroupColumn(gcd, dc, this.data);
                columns.Add(gc);
            }
        }

        public void Save() {
            this.data.SaveGroup(this.Id, this.Name, this.Identyficator, this.EnableTranslate, this.ReadOnly);
        }

        public void Delete() {
            if (this.Id > 0)
                this.data.DeleteGroup(this.Id);
            this.Id = 0;
        }

        public IGroup[] GetGroups() {
            if (this.instance.GetGroups() == null)
                this.instance.SetGroups(this.ReadGroups());
            return this.instance.GetGroups();
        }

        public bool CheckName(string checkName) {
            return this.data.CheckGroupName(checkName);
        }

        private IGroup[] ReadGroups() {
            GroupData[] data = this.data.GetAllGroups();
            return data.Select(gd => new DaGroup(this.data, gd)).ToArray();
        }

        private ImpGroup ReadInstance(int groupId) {
            string cKey = "ImpGroup" + groupId;
            MemoryCache cache = MemoryCache.Default;
            if (cache.Contains(cKey))
                return cache[cKey] as ImpGroup;
            else {
                GroupData gData = this.data.GetGroup(groupId);
                ImpGroup group = this.ReadInstance(gData);
                CacheItemPolicy cip = new CacheItemPolicy();
                cip.ChangeMonitors.Add(new NemoChangeMonitor(0));
                cip.SlidingExpiration = TimeSpan.FromMinutes(10);
                CacheItem ci = new CacheItem(cKey, group);
                cache.Add(ci, cip);
                return group;
            }
        }

        private ImpGroup ReadInstance(GroupData data) {
            ImpGroup inst = new ImpGroup();
            if (data.Id > 0) {
                inst.Id = data.Id;
                inst.ParentId = data.ParentId;
                inst.Name = data.Name;
                inst.SystemName = data.SystemName;
                if (string.IsNullOrEmpty(inst.Name)) inst.Name = inst.SystemName;
                inst.Identyficator = data.Identyficator;
                inst.Exists = true;
                inst.SelectTable = data.SelectTable;

                GroupType t = GroupType.Table;
                if (Enum.TryParse<GroupType>(data.Type, out t))
                    inst.Type = t;
                inst.EnableTranslate = data.EnableTranslate;
                inst.ReadOnly = data.ReadOnly;
            }
            return inst;
        }

        public IGroupRecord[] GetGroupRecords(CultureInfo language) {
            GroupRecordQuery q = new GroupRecordQuery(this);
            return this.GetGroupRecords(q, language);
        }

        public IGroupRecord[] GetGroupRecords(GroupRecordQuery query, CultureInfo language) {
            GroupRecordData[] records = null;
            if (query.type == GroupRecordQuery.QueryType.QueryWithWhereClauses)
                records = this.data.GetGroupRecords(query.SelectColumns, query.WhereClauses, this.GetFromGroups(), language.Name);
            else
                records = this.data.GetGroupRecords(query.SelectColumns, query.query, this.GetFromGroups(), language.Name);
            var grs = records.Select(grd => new DaGroupRecord(this.Id, this.data, grd));

            return grs.ToArray();
        }

        private FromGroup[] GetFromGroups() {
            switch (this.Type) {
                case GroupType.Table:
                case GroupType.View:
                    return new FromGroup[] { new FromGroup(this.Id, this.SelectTable, this.Type.ToString()) };
                case GroupType.ExtendedTable:
                    IGroup parentGroup = Factory.Instance().GetGroup(this.instance.ParentId);
                    return new FromGroup[] { 
                        new FromGroup(parentGroup.Id, parentGroup.SelectTable, parentGroup.Type.ToString()),
                        new FromGroup(this.Id,this.SelectTable,this.Type.ToString(),parentGroup.SelectTable,"idx","idx")
                    };
            }
            return new FromGroup[0];
        }

        public IGroupRecord GetGroupRecord(int recordId, CultureInfo language) {
            GroupRecordQuery q = new GroupRecordQuery(this);
            GroupRecordData record = this.data.GetGroupRecord(q.SelectColumns, recordId, this.GetFromGroups(), language.Name);
            if (record != null) {
                return new DaGroupRecord(this.Id, this.data, record);
            }
            else
                return null;
        }

        public IGroupRecord[] GetRelatedGroupRecords(int relGroupId, string columnName, int relRecordId, CultureInfo language) {
            //GroupRecordQuery q = new GroupRecordQuery(this);
            //GroupRecordData[] records = this.data.GetRelatedGroupRecords(q.SelectColumns, this.Id, columnName, relGroupId, relRecordId, language.Name, this.Type.ToString());
            //return records.Select(grd => new DaGroupRecord(this.Id, this.data, grd)).ToArray();
            return this.GetRelatedGroupRecords(Factory.Instance().GetGroup(relGroupId), columnName, relRecordId, language);
        }

        public IGroupRecord[] GetRelatedGroupRecords(IGroup parentGroup, string columnName, int relRecordId, CultureInfo language) {
            GroupRecordQuery q = new GroupRecordQuery(this);
            ISupportSystemNames sysData = this.data as ISupportSystemNames;
            string joinTableName = String.Empty;
            if (sysData != null)
                joinTableName = sysData.Names.GetSelectNameForRelation(this.Id, columnName, parentGroup.Id);
            else
                joinTableName = string.Format("{0}_{1}", parentGroup.SelectTable, columnName);
            FromGroup joinGroup = new FromGroup(parentGroup.Id, joinTableName, "FN", this.SelectTable, "rel", "idx");
            joinGroup.LimitToIdx = relRecordId;
            FromGroup[] groups = new FromGroup[] {
                new FromGroup(this.Id, this.SelectTable, this.Type.ToString()),
                joinGroup
            };
            GroupRecordData[] records = this.data.GetRelatedGroupRecords(q.SelectColumns, groups, new WhereColumn[0], language.Name);
            //GroupRecordData[] records = this.data.GetRelatedGroupRecords(q.SelectColumns, this.Id, columnName, relGroupId, relRecordId, language.Name, this.Type.ToString());
            return records.Select(grd => new DaGroupRecord(this.Id, this.data, grd)).ToArray();
        }

        public void RemoveRelationToGroupRecord(int relRecordId, int parentGroupId, string parentColumnName, int parentRecordId) {
            this.data.RemoveRelationToGroupRecord(this.Id, relRecordId, parentGroupId, parentColumnName, parentRecordId);
        }

        public void AddRelationToGroupRecord(int relRecordId, int parentGroupId, string parentColumnName, int parentRecordId) {
            this.data.AddRelationToGroupRecord(this.Id, relRecordId, parentGroupId, parentColumnName, parentRecordId, Factory.Instance().GetCurrentUser().UserId);
        }

        public int GetGroupId(string groupSystemName) {
            if (this.instance.GroupSystemNames == null)
                this.instance.GroupSystemNames = this.data.GetGroupSystemNames();
            return this.instance.GetGroupId(groupSystemName);
        }

        public int CreateGroup(string systemName) {
            return this.data.CreateGroup(systemName);
        }

        public bool CheckViewDeclaration(string selectDeclaration) {
            return this.data.CheckViewDeclaration(selectDeclaration);
        }

        public int CreateView(string systemName, string selectDeclaration) {
            return this.data.CreateView(systemName, selectDeclaration);
        }

        #endregion

    }
}
